#include <stdlib.h>
#include <assert.h>
#include <math.h>

#include "cache.h"

pcg_cache_t *pcg_cache_create(unsigned int width, unsigned int height, unsigned int depth)
{
	pcg_cache_t *cache;
	cache = (pcg_cache_t*) malloc(sizeof(pcg_cache_t));
	if (cache == NULL) return NULL;
	
	if (width == 0 || height == 0 || depth == 0) {
		return NULL;
	}
	
	cache->size[0] = width;
	cache->size[1] = height;
	cache->size[2] = depth;
	cache->data = (float *) malloc(width * height * depth * sizeof(float));
	return cache;
}

void pcg_cache_delete(pcg_cache_t *cache) {
	assert(cache != NULL);
	free(cache->data);
	free(cache);
}

void pcg_cache_fill1(pcg_cache_t *cache, pcg_noise1_fn_t prim, int iz, int iy, float xo, float xs) {
	float xd; // delta-x
	int i, n = iz * cache->size[0] * cache->size[1] + iy * cache->size[0];
	xd = (xs - xo) / cache->size[0];
	for (i = 0; i < cache->size[0]; i++) {
		cache->data[n++] = prim(xo);
		xo += xd;
	}
	
}

void pcg_cache_fill2(pcg_cache_t *cache, pcg_noise2_fn_t prim, int iz,
	float xo, float yo, float xs, float ys) {
	
	float xd, yd, temp;
	int i, j, n = iz * cache->size[0] * cache->size[1];
	xd = (xs - xo) / cache->size[0];
	yd = (ys - yo) / cache->size[1];
	temp = yo;
	for (j = 0; j < cache->size[1]; j++) {
		for (i = 0; i < cache->size[0]; i++) {
			cache->data[n++] = prim(xo, yo);
			xo += xd;
		}
		yo += yd;
		xo = temp;
	}
}

void pcg_cache_fill3(pcg_cache_t *cache, pcg_noise3_fn_t prim, 
	float xo, float yo, float zo, float xs, float ys, float zs) {
	
	float xd, yd, zd, temp1, temp2;
	int i, j, k, n = 0;
	xd = (xs - xo) / cache->size[0];
	yd = (ys - yo) / cache->size[1];
	zd = (zs - zo) / cache->size[2];
	temp1 = yo; temp2 = xo;
	for (k = 0; k < cache->size[2]; k++) {
		for (j = 0; j < cache->size[1]; j++) {
			for (i = 0; i < cache->size[0]; i++) {
				cache->data[n++] = prim(xo, yo, zo);
				xo += xd;
			}
			yo += yd;
			xo = temp2;
		}
		zo += zd;
		yo = temp1;
	}
}

void pcg_cache_fill_spheremap(pcg_cache_t *cache, pcg_noise3_fn_t prim, int iz, float xo, float yo, float zo, float r) {
	float theta, rho, dtheta, drho;
	int i, j, n = 0;
	
	theta = 0.0; rho = - M_PI / 2.0;
	dtheta = 2.0 * M_PI / cache->size[0];
	drho = M_PI / cache->size[1];
	
	for (i = 0; i < cache->size[1]; i++) {
		for (j = 0; j < cache->size[0]; j++) {
			float x = xo + r * cos(theta) * sin(rho);
			float y = yo + r * sin(theta) * sin(rho);
			float z = zo + r * cos(rho);
			cache->data[n++] = prim(x, y, z);
			theta += dtheta;
		}
		rho += drho;
		theta = 0.0;
	}
}